package com.example.springboottest.example.jedis.pool;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;

/**
 * （1）maxTotal：资源池中最大的连接数，默认值为 8。
 * （2）maxIdle：资源池允许最大空闲的连接数，默认值为 8。
 * （3）minIdle：资源池确保最少空闲的连接数，默认值为 0。
 * （4）blockWhenExhausted：当资源池用尽后，调用者是否要等待，默认值为 true。当为 true时，maxWaitMillis 才会生效。
 * （5）maxWaitMillis：当资源池连接用尽后，调用者的最大等待时间（单位为毫秒）。默认值为-1，表示永不超时，不建议使用默认值。
 * （6）testOnBorrow：向资源池借用连接时，是否做有效性检测（ping 命令），如果是无效连接，会被移除，默认值为 false，
 * 表示不做检测。如果为 true，则得到的 Jedis 实例均是可用的。在业务量小的应用场景，建议设置为 true，确保连接可用；
 * 在业务量很大的应用场景，建议设置为 false （默认值），少一次 ping 命令的开销，有助于提升性能。
 * （7）testOnReturn：向资源池归还连接时，是否做有效性检测（ping 命令），如果是无效连接，会被移除，默认值为 false，表示不做检测。
 * 同样，在业务量很大的应用场景，建议设置为 false（默认值），少一次 ping 命令的开销。
 * （8）testWhileIdle：如果为 true，表示用一个专门的线程对空闲的连接进行有效性的检测扫描，
 * 如果有效性检测失败，即表示无效连接，会从资源池中移除。默认值为 true *
 * <p>
 * 有个实际的问题：如何推算一个连接池的最大连接数 maxTotal 呢？
 * 实际上，这是一个很难精准回答的问题，主要是依赖的因素比较多。
 * 如何推算单个 Jedis 连接的 QPS 呢？假设一个 Jedis 命令操作的时间约为 5ms（包含 borrow + return + Jedis 执行命令 + 网络延迟），
 * 那么，单个 Jedis 连接的 QPS 大约是 1000/5 =200。如果业务期望的 QPS 是 100000，则需要的最大连接数为 100000/200 =500。
 * 大致的推算方法是：业务 QPS/单连接的 QPS = 最大连接数。
 * <p>
 * 如何推算连接池的最大空闲连接数 maxIdle 值呢？
 * 实际上，maxTotal 只是给出了一个连接数量的上限，maxIdle 实际上才是业务可用的最大连接数，从这个层面来说，maxIdle 不能设置过小，
 * 否则会有创建、销毁连接的开销。使得连接池达到最佳性能的设置是 maxTotal = maxIdle，
 * 应尽可能地避免由于频繁地创建和销毁 Jedis 连接所带来的连接池性能的下降。
 */
public class JredisPoolBuilder {
    public static final int MAX_IDLE = 50;
    public static final int MAX_TOTAL = 50;
    private static JedisPool pool = null;

    static {
        //创建连接池
        buildPool();
        //预热连接池
        hotPool();
    }

    //创建连接池
    private static JedisPool buildPool() {
        if (pool == null) {
            long start = System.currentTimeMillis();
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_TOTAL);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(1000 * 10);
            // 在borrow一个jedis实例时，是否提前进行validate操作；
            // 如果为true，则得到的jedis实例均是可用的；
            config.setTestOnBorrow(true);
            //new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
            pool = new JedisPool(config, "127.0.0.1", 6379, 10000);
            long end = System.currentTimeMillis();
            System.out.println("buildPool  毫秒数:" + (end - start));
        }
        return pool;
    }

    //获取连接
    public synchronized static Jedis getJedis() {
        return pool.getResource();
    }

    //连接池的预热
    public static void hotPool() {

        long start = System.currentTimeMillis();
        List<Jedis> minIdleJedisList = new ArrayList<Jedis>(MAX_IDLE);
        Jedis jedis = null;

        for (int i = 0; i < MAX_IDLE; i++) {
            try {
                jedis = pool.getResource();
                minIdleJedisList.add(jedis);
                jedis.ping();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            } finally {
            }
        }
        //pool池中获取的jedis，close不会真正的关闭，只会返回给pool
        for (int i = 0; i < MAX_IDLE; i++) {
            try {
                jedis = minIdleJedisList.get(i);
                jedis.close();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            } finally {

            }
        }
        long end = System.currentTimeMillis();
        System.out.println("hotPool  毫秒数:" + (end - start));
    }
}
